﻿#!/usr/bin/env python
# -*- coding: utf-8 -*-

# widgets.py

# Copyright 2009-2010 Diego Hansen Hahn (aka DiegoHH) <diegohh90 [at] hotmail [dot] com>

# lazynds is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License.

# lazynds is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with lazynds. If not, see <http://www.gnu.org/licenses/>.

import gtk
import gobject
import cairo
import math

import ctypes

LEFT_BUTTON = 1
RIGHT_BUTTON = 3

class Container(gtk.VBox):
    def __init__(self, child,layout,wtree, pos, resize = False):
        gtk.VBox.__init__(self)
        x, y = pos

        self.wtree = wtree

        self.top_border = gtk.DrawingArea()
        self.top_border.add_events(gtk.gdk.BUTTON_PRESS_MASK
                                   |gtk.gdk.BUTTON_RELEASE_MASK
                                   |gtk.gdk.POINTER_MOTION_MASK
                                   |gtk.gdk.BUTTON_MOTION_MASK)

        self.top_border.connect('expose-event', self.do_top_border_expose_event)
        self.top_border.connect('button-press-event', self.do_button_press_event)
        self.top_border.connect('button-release-event', self.do_button_release_event)
        self.top_border.connect('motion-notify-event', self.do_motion_notify_event)
        self.top_border.set_size_request(150,4)

        self.bottom_border = gtk.DrawingArea()
        self.bottom_border.add_events(gtk.gdk.BUTTON_PRESS_MASK
                                   |gtk.gdk.BUTTON_RELEASE_MASK
                                   |gtk.gdk.POINTER_MOTION_MASK
                                   |gtk.gdk.BUTTON_MOTION_MASK)
        self.bottom_border.connect('expose-event', self.do_bottom_border_expose_event)
        if resize:
            self.bottom_border.connect('button-press-event', self.do_bottom_border_button_press_event)
            self.bottom_border.connect('button-release-event', self.do_bottom_border_button_release_event)
            self.bottom_border.connect('motion-notify-event', self.do_bottom_border_motion_notify_event)

        self.bottom_border.set_size_request(150,4)

        self.add_events(gtk.gdk.EXPOSURE_MASK)
        self.pack_start(self.top_border, True, True, 0)

        self.layout = layout
        self.layout.put(self, x,y)
        self.child = child
        self.pack_start(child, True, True, 0)
        self.pack_start(self.bottom_border, True, True, 0)

        self.lock_on_screen = True
        self.position = None

        self._hide = False
        self.bring_to_front = True

    def do_top_border_expose_event(self, widget, event):
        width, height = widget.allocation.width, widget.allocation.height
        cr = event.window.cairo_create()
        cr.rectangle(event.area.x, event.area.y,
                     event.area.width, event.area.height)
        cr.clip()
        cr.set_source_rgb(0.1, 0.1, 0.1)
        radius = height
        cr.arc(radius, radius, radius, math.pi, 1.5*math.pi)
        cr.line_to(width-radius, 0)
        cr.arc(width-radius, radius, radius, 1.5*math.pi, 2*math.pi)
        cr.fill()

    def do_bottom_border_expose_event(self, widget, event):
        width, height = widget.allocation.width, widget.allocation.height
        cr = event.window.cairo_create()
        cr.rectangle(event.area.x, event.area.y,
                     event.area.width, event.area.height)
        cr.clip()
        cr.set_source_rgb(0.1, 0.1, 0.1)
        radius = height
        cr.arc_negative(radius, 0, radius, math.pi, 0.5*math.pi)
        cr.line_to(width-radius, height)
        cr.arc_negative(width-radius, 0, radius, 0.5*math.pi, 0)
        cr.fill()

    def do_bottom_border_button_press_event(self, widget, event):

        self.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.BOTTOM_RIGHT_CORNER))

        if self.bring_to_front:
            for x in self.get_children():
                x.window.raise_()
        self.lock_on_screen = False
        self.position = (int(self.allocation.width-event.x), int(self.allocation.height-event.y))
        self.grab_add()

    def do_bottom_border_button_release_event(self, widget, event):

        self.window.set_cursor(None)

        self.lock_on_screen = True
        self.position = None
        self.grab_remove()

    def do_bottom_border_motion_notify_event(self, widget, event):
        if not self.lock_on_screen and not self._hide:
            x_off, y_off = self.position
            x, y = self.translate_coordinates(self, int(event.x), int(event.y))
            y = self.wtree.get_property('height-request') + y
            self.wtree.set_size_request(x if x >= 80 else 80, y if y >= 20 else 20)


    def do_button_press_event(self, widget, event):
        if self.bring_to_front:
            for x in self.get_children():
                x.window.raise_()
        if event.button == LEFT_BUTTON:

            self.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.FLEUR))

            self.lock_on_screen = False
            self.position = (event.x, event.y)
            self.grab_add()
        else:
            if self._hide:
                self.child.show()
                self._hide = False
            else:
                self.child.hide()
                self._hide = True

    def do_button_release_event(self, widget, event):

        self.window.set_cursor(None)

        if event.button == LEFT_BUTTON:
            self.lock_on_screen = True
            self.position = None
            self.grab_remove()

    def do_motion_notify_event(self, widget, event):
        if not self.lock_on_screen:
            x, y = self.translate_coordinates(self.parent, int(event.x), int(event.y))
            x -= self.position[0]
            y -= self.position[1]
            self.parent.move(self, int(x), int(y))

    def get_property(self, name):
        if name == 'size':
            return (int(self.wtree.allocation.width), int(self.wtree.allocation.height))
        elif name == 'position':
            return (self.parent.child_get_property(self, 'x'), self.parent.child_get_property(self, 'y'))

    def set_property(self, name, value):
        if name == 'size':
            self.wtree.set_size_request(*value)
        elif name == 'position':
            self.parent.move(self, value[0], value[1])

class BuilderContainer(gtk.Viewport):
    '''
    Construtor para árvore de widgets carregadas pelo GtkBuilder
    '''
    def __init__(self, layout, wtree, x, y, resize = False):
        gtk.Viewport.__init__(self)
        self.set_shadow_type(gtk.SHADOW_ETCHED_IN)
        self.layout = layout
        self.container = Container(self, self.layout, wtree,(x,y), resize)
        wtree.reparent(self)

class ChildContainer(gtk.Viewport):
    '''
    Construtor para árvore de widgets geradas pelo lazy
    '''
    def __init__(self, layout, wtree, x, y, resize = False):
        gtk.Viewport.__init__(self)
        self.set_shadow_type(gtk.SHADOW_ETCHED_IN)
        self.layout = layout
        self.container = Container(self, self.layout,(x,y), resize)
        self.container.bring_to_front = False
        self.add(wtree)

class HexSpinButton(gtk.SpinButton):
    '''
    SpinButton modificado para mostrar números no formato hexadecimal
    '''
    def __init__(self):
        gtk.SpinButton.__init__(self)
        self.connect('input', self.input)
        self.connect('output', self.output)
        self.props.digits = 1
        self.show()

    def input(self, spinbutton, gpointer):
        text = spinbutton.get_text()
        double = ctypes.c_double.from_address(hash(gpointer))
        double.value = int(text, 16)
        return True

    def output(self, spinbutton):
        text = '%08.*X' % (int(spinbutton.props.digits), spinbutton.props.adjustment.value)
        spinbutton.set_text(text)
        return True

class List(gtk.TreeView):

    def __init__(self, title):
        gtk.TreeView.__init__(self)

        self.list_store = gtk.TreeStore(gobject.TYPE_STRING, gobject.TYPE_BOOLEAN)

        str_renderer = gtk.CellRendererText()
        chk_renderer = gtk.CellRendererToggle()
        chk_renderer.set_property('activatable', True)
        chk_renderer.connect('toggled', self.on_chk_renderer_toggled, self.list_store )

        str_column = gtk.TreeViewColumn(title, str_renderer, text=0)
        chk_column = gtk.TreeViewColumn(None, chk_renderer)
        chk_column.add_attribute(chk_renderer, 'active', 1)

        self.append_column(chk_column)
        self.append_column(str_column)

        setattr(self, 'active_list', list())
        setattr(self, 'items', dict())

        self.show()

    def append(self, key, value):

        self.items.update({key:value})
        self.list_store.append(None, (key, None))

        self.set_model(self.list_store)

    def on_chk_renderer_toggled(self, cell, path, model):
        model[path][1] = not model[path][1]
        if self.items[model[path][0]] not in self.active_list:
            self.active_list.append(self.items[model[path][0]])
        else:
            self.active_list.remove(self.items[model[path][0]])

